package cn.portx.frame.service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.bstek.bdf3.dorado.jpa.JpaUtil;
import com.bstek.bdf3.saas.Constants;
import com.bstek.bdf3.saas.SaasUtils;
import com.bstek.bdf3.saas.domain.Organization;
import com.bstek.bdf3.saas.service.DataSourceService;
import com.bstek.bdf3.saas.service.EntityManagerFactoryService;
import com.bstek.bdf3.security.orm.OrganizationSupport;
import com.bstek.bdf3.security.orm.User;
import com.bstek.bdf3.security.service.GrantedAuthorityService;
import com.bstek.dorado.data.entity.EntityState;
import com.bstek.dorado.data.entity.EntityUtils;
import com.bstek.dorado.view.resolver.ClientRunnableException;

import cn.portx.frame.entity.Company;
import cn.portx.frame.entity.UserEntity;

@Aspect
@Component("cams.UserDetailsServiceImpl")
public class UserDetailsServiceImpl {

	@Autowired
	private EntityManagerFactoryService entityManagerFactoryService;

	@Autowired
	private GrantedAuthorityService grantedAuthorityService;

	@Autowired
	private OrganizationDBHelper organizationDBHelper;

	@Autowired
	private DataSourceService dataSourceService;

	@Autowired
	private DataSourceProperties properties;

//	@Around("execution(* cn.portx.frame.service.SaveUserWhenSyncServiceImpl.save(..))")
//	public void saveUserWhenSync(ProceedingJoinPoint point) throws Throwable {
//		this.saveUser(point);
//	}

	@Around("execution(* com.bstek.bdf3.security.ui.service.UserService.save(..))")
	public void saveUser(ProceedingJoinPoint point) throws Throwable {
		Object obj = point.getArgs()[0];
		List<String> list = new ArrayList<String>();
		if (obj instanceof User) {
			User user = (User) obj;
			if (EntityUtils.getState(user) == EntityState.NEW || !EntityUtils.isEntity(user)) {
				list.add(user.getUsername());
			}
		} else if (obj instanceof List) {
			List<User> users = (List<User>) obj;
			for (User user : users) {
				if (EntityUtils.getState(user) == EntityState.NEW || !EntityUtils.isEntity(user)) {
					list.add(user.getUsername());
				}
			}
		}
//		List<Organization> orgList = JpaUtil.linq(Organization.class).list();
		for (String username : list) {
			if (JpaUtil.linq(UserEntity.class).idEqual(username).exists()) {
				String message = "用户名【" + username + "】已存在，请重新指定！";
				throw new ClientRunnableException(message, "dorado.MessageBox.alert('" + message + "')");
			}
//			ForkJoinPool forkJoinPool = new ForkJoinPool();
//			GetUserTask task = new GetUserTask(orgList, username);
//			Future<User> resultFuture = forkJoinPool.submit(task);
//			try {
//				User user = resultFuture.get();
//				if (user != null) {
//					throw new ClientRunnableException("dorado.MessageBox.alert('用户名【" + username + "】已存在，请重新指定！')");
//				}
//			} catch (InterruptedException | ExecutionException e) {
//				throw new RuntimeException(e);
//			}
		}
		point.proceed();
		//将User保存到master库
		if (obj instanceof User) {
			User user = (User) obj;
			if (EntityUtils.getState(user) == EntityState.NEW || !EntityUtils.isEntity(user)) {
				this.insertUser(user);
			} else if (EntityUtils.getState(user) == EntityState.MODIFIED) {
				this.updateUser(user);
			} else if (EntityUtils.getState(user) == EntityState.DELETED) {
				this.deleteUser(user);
			}
		} else if (obj instanceof List) {
			for (User user : (List<User>) obj) {
				if (EntityUtils.getState(user) == EntityState.NEW || !EntityUtils.isEntity(user)) {
					this.insertUser(user);
				} else if (EntityUtils.getState(user) == EntityState.MODIFIED) {
					this.updateUser(user);
				} else if (EntityUtils.getState(user) == EntityState.DELETED) {
					this.deleteUser(user);
				}
			}
		}
	}

	private void insertUser(User user) {
		UserEntity ue = new UserEntity();
		ue.setUsername(user.getUsername());
		ue.setAccountNonExpired(user.isAccountNonExpired());
		ue.setAccountNonLocked(user.isAccountNonLocked());
		ue.setAdministrator(user.isAdministrator());
		ue.setAuthorities(user.getAuthorities());
		ue.setCredentialsNonExpired(user.isCredentialsNonExpired());
		ue.setEnabled(user.isEnabled());
		ue.setNickname(user.getNickname());
		ue.setOrganization(this.getOrganization(user));
		ue.setPassword(user.getPassword());

		ue.setOrgId(((Organization) ue.getOrganization()).getId());

		this.organizationDBHelper.doDb(Constants.MASTER, false, (org) -> {
			JpaUtil.persistAndFlush(ue);
			return null;
		});
	}

	private void updateUser(User user) {
		UserEntity ue = new UserEntity();
		ue.setUsername(user.getUsername());
		ue.setAccountNonExpired(user.isAccountNonExpired());
		ue.setAccountNonLocked(user.isAccountNonLocked());
		ue.setAdministrator(user.isAdministrator());
		ue.setAuthorities(user.getAuthorities());
		ue.setCredentialsNonExpired(user.isCredentialsNonExpired());
		ue.setEnabled(user.isEnabled());
		ue.setNickname(user.getNickname());
		ue.setOrganization(this.getOrganization(user));
		ue.setPassword(user.getPassword());
		ue.setOrgId(((Organization) ue.getOrganization()).getId());

		this.organizationDBHelper.doDb(Constants.MASTER, false, (org) -> {
			JpaUtil.mergeAndFlush(ue);
			return null;
		});
	}

	private void deleteUser(User user) {
		this.organizationDBHelper.doDb(Constants.MASTER, false, (org) -> {
			JpaUtil.lind(UserEntity.class).idEqual(user.getUsername()).delete();
			return null;
		});
	}

	private Organization getOrganization(User user) {
		Organization org = user.getOrganization();
		if (org == null) {
			OrganizationSupport os = (OrganizationSupport) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			org = os.getOrganization();
		}
		return org;
	}

	@Around("execution(* com.bstek.bdf3.security.ui.service.UserService.changePassword(..))")
	public void changePassword(ProceedingJoinPoint point) throws Throwable {
		Object[] args = point.getArgs();
		String username = (String) args[0];
		String newPassword = (String) args[1];

		point.proceed();

//		UserEntity ue = JpaUtil.linq(UserEntity.class).idEqual(username).list().get(0);
		String orgId = (String) JpaUtil.linq(UserEntity.class,String.class).select("orgId").idEqual(username).list().get(0);
		User u = JpaUtil.getOne(User.class, username);
		Organization organization = new Organization();
		organization.setId(orgId);
		u.setOrganization(organization);
		this.updateUser(u);
	}

	@Around("execution(* org.springframework.security.core.userdetails.UserDetailsService.loadUserByUsername(..))")
	public UserDetails loadUserByUsername(ProceedingJoinPoint point) throws Throwable {
		Object[] args = point.getArgs();
		String username = (String) args[0];

		List<UserEntity> users = JpaUtil.linq(UserEntity.class).idEqual(username).list();
		if (users.isEmpty()) {
			throw new UsernameNotFoundException("没有找到用户！");
		}
		UserEntity ue = users.get(0);
		Organization org = null;

		if (!StringUtils.equals(Constants.MASTER, ue.getOrgId())) {
			List<Company> companies = JpaUtil.linq(Company.class).collect(Organization.class, "orgId").equal("enable", "1").equal("orgId", ue.getOrgId()).list();
			if (companies == null || companies.isEmpty()) {
				throw new IllegalStateException("公司尚未启用！");
			}
			org = companies.get(0).getOrg();
		} else {
			org = new Organization();
			org.setId(Constants.MASTER);
		}
		ue.setOrganization(org);

		User user = new User();
		user.setUsername(ue.getUsername());
		user.setAccountNonExpired(ue.isAccountNonExpired());
		user.setAccountNonLocked(ue.isAccountNonLocked());
		user.setAdministrator(ue.isAdministrator());
		user.setAuthorities(ue.getAuthorities());
		user.setCredentialsNonExpired(ue.isCredentialsNonExpired());
		user.setEnabled(ue.isEnabled());
		user.setNickname(ue.getNickname());
		user.setOrganization(ue.getOrganization());
		user.setPassword(ue.getPassword());

		SaasUtils.doQuery(org.getId(), () -> {
			user.setAuthorities(grantedAuthorityService.getGrantedAuthorities(user));
		});
		return user;

//		try {
//			SaasUtils.pushSecurityContext((Organization) user.getOrganization());
//			SecurityUserUtil.setAuthorities(user, grantedAuthorityService.getGrantedAuthorities(user));
////			this.runScripts((Organization) user.getOrganization());
//			return user;
//		} finally {
//			SaasUtils.popSecurityContext();
//		}

//		List<Organization> orgList = JpaUtil.linq(Organization.class).list();
//		ForkJoinPool forkJoinPool = new ForkJoinPool();
//		GetUserTask task = new GetUserTask(orgList, username);
//		Future<User> resultFuture = forkJoinPool.submit(task);
//		try {
//			User user = resultFuture.get();
//			if (user == null) {
//				throw new UsernameNotFoundException("没有找到用户");
//			}
//			SecurityUserUtil.setAuthorities(user, grantedAuthorityService.getGrantedAuthorities(user));
//			return user;
//		} catch (InterruptedException | ExecutionException e) {
//			throw new RuntimeException(e);
//		}
	}

//	@Transactional(readOnly = true)
//	private EntityManager getEntityManager(Organization org) {
//		EntityManagerFactory emf = entityManagerFactoryService.getOrCreateEntityManagerFactory(org);
//		EntityManager em = EntityManagerFactoryUtils.getTransactionalEntityManager(emf);
//		if (em == null) {
//			em = emf.createEntityManager();
//		} else {
//			System.out.println("*************************************************");
//			System.out.println("*************************************************");
//		}
//		return em;
//	}

//	@SuppressWarnings("serial")
//	private class GetUserTask extends RecursiveTask<User> {
//
//		private int threshold = 4;//阀值，表示多少个连接构成一组
//		private List<Organization> orgList;
//		private String username;
//		private List<GetUserTask> tasks = new ArrayList<GetUserTask>();
//
//		public GetUserTask(List<Organization> orgList, String username) {
//			this.orgList = orgList;
//			this.username = username;
//		}
//
//		@Override
//		protected User compute() {
//			User user = null;
//			if (this.orgList.size() <= threshold) {
//				for (Organization org : orgList) {
//					user = getEntityManager(org).find(User.class, this.username);
//					if (user != null) {
//						user.setOrganization(org);
//						break;
//					}
//				}
//			} else {
//				//分割任务
//				int count = (int) Math.ceil((double) this.orgList.size() / threshold);//任务数
//				for (int i = 0; i < count; i++) {
//					int fromIndex = i * threshold;
//					int endIndex = fromIndex + threshold;
//					endIndex = endIndex > orgList.size() ? orgList.size() : endIndex;
//					GetUserTask task = new GetUserTask(orgList.subList(fromIndex, endIndex), this.username);
//					tasks.add(task);
//					task.fork();
//				}
//				for (GetUserTask task : tasks) {
//					user = task.join();
//					if (user != null) {
//						for (GetUserTask t : tasks) {
//							if (!t.equals(task)) {
//								t.completeExceptionally(new UsernameNotFoundException("没有找到用户"));
//							}
//						}
//						break;
//					}
//				}
//			}
//			return user;
//		}
//
//	}
//
//	private void runScripts(Organization org) {
//		try {
//			DataSource dataSource = dataSourceService.getDataSource(org);
//			ClassPathResource resource = new ClassPathResource("cn/portx/frame/service/alter.sql");
//
//			ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
//			populator.setContinueOnError(this.properties.isContinueOnError());
//			populator.setSeparator(this.properties.getSeparator());
//			if (this.properties.getSqlScriptEncoding() != null) {
//				populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name());
//			}
//			populator.addScript(resource);
//			DatabasePopulatorUtils.execute(populator, dataSource);
//		} catch (Throwable e) {
//
//		}
//	}

}
